import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Patch

# Data for the chart
metrics = ["CPU Usage (%)", "Memory (MB)", "Thread Number"]

# Data for three test cases
test_cases = ["test_case1", "test_case2", "test_case3"]
selenium_values = [
    [5.27, 1139.60, 221.45],  # test_case1
    [8.92, 1180.62, 229.61],  # test_case2
    [6.06, 1373.89, 243.43]   # test_case3
]
cypress_values = [
    [6.11, 2074.68, 320.49],  # test_case1
    [22.57, 2489.66, 361.64],  # test_case2
    [14.91, 2506.83, 368.35]   # test_case3
]

bar_width = 0.4  # Width for each group of bars
spacing = len(metrics) + 1  # Additional spacing between test cases

# Calculate x positions for the bars
test_case_offsets = [i * spacing for i in range(len(test_cases))]
x_positions = []
for offset in test_case_offsets:
    x_positions.extend(np.arange(offset, offset + len(metrics)))

x_positions = np.array(x_positions)  # Ensure positions are a numpy array

# Creating the plot
fig, ax1 = plt.subplots(figsize=(12, 6))
ax1.set_ylim(0, 25)
# Secondary y-axis for memory
ax2 = ax1.twinx()
ax2.spines['left'].set_position(('outward', 60))  # Offset memory axis to the left

# Third y-axis for thread numbers
ax3 = ax1.twinx()
ax3.spines['right'].set_position(('outward', 60))  # Offset thread axis to the right
ax3.set_ylim(0, 450)

# Plot bars for each test case
for i, test_case in enumerate(test_cases):
    base_idx = i * len(metrics)  # Base index for the test case

    # CPU Usage
    bars_selenium_cpu = ax1.bar(x_positions[base_idx] - bar_width / 2, selenium_values[i][0], bar_width, color='steelblue', hatch='//')
    bars_cypress_cpu = ax1.bar(x_positions[base_idx] + bar_width / 2, cypress_values[i][0], bar_width, color='darkorange', hatch='\\')

    # Memory
    bars_selenium_memory = ax2.bar(x_positions[base_idx + 1] - bar_width / 2, selenium_values[i][1], bar_width, color='steelblue', hatch='//')
    bars_cypress_memory = ax2.bar(x_positions[base_idx + 1] + bar_width / 2, cypress_values[i][1], bar_width, color='darkorange', hatch='\\')

    # Thread Number
    bars_selenium_thread = ax3.bar(x_positions[base_idx + 2] - bar_width / 2, selenium_values[i][2], bar_width, color='steelblue', hatch='//')
    bars_cypress_thread = ax3.bar(x_positions[base_idx + 2] + bar_width / 2, cypress_values[i][2], bar_width, color='darkorange', hatch='\\')

    # Adding exact values on top of each bar
    for container, axis in zip(
            [bars_selenium_cpu, bars_cypress_cpu, bars_selenium_memory, bars_cypress_memory, bars_selenium_thread,
             bars_cypress_thread],
            [ax1, ax1, ax2, ax2, ax3, ax3]
    ):
        for bar in container:
            height = bar.get_height()
            axis.text(bar.get_x() + bar.get_width() / 2, height,
                      f'{height:.2f}', ha='center', va='bottom', fontsize=8)

# Adding labels and titles
ax1.set_xlabel('Test Cases and Metrics')
ax1.set_ylabel('CPU Usage (%)', color='steelblue')
ax2.set_ylabel('Memory (MB)', color='darkorange')
ax3.set_ylabel('Thread Number', color='green')
ax1.set_title('Performance Comparison of Selenium and Cypress Across Test Cases')

# Customizing x-axis labels
test_case_labels = []
for test_case in test_cases:
    test_case_labels.extend([f'{test_case}\n{metric}' for metric in metrics])
ax1.set_xticks(x_positions)
ax1.set_xticklabels(test_case_labels, rotation=45, ha='right', fontsize=9)

# Adding a single legend for bar styles
handles = [
    Patch(facecolor='steelblue', edgecolor='black', hatch='//', label='Selenium'),
    Patch(facecolor='darkorange', edgecolor='black', hatch='\\', label='Cypress')
]
fig.legend(handles=handles, loc=(0, 1), title='Tools', frameon=False)

# Adjust layout and display the chart
plt.tight_layout()
plt.show()
